﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Cci;

namespace PartialExpressionCompleter.Util
{
    class NamespaceCollector : CodeTraverser
    {
        private HashSet<INamespaceDefinition> nss = new HashSet<INamespaceDefinition>();

        public NamespaceCollector(INamespaceDefinition ns)
        {
            nss.Add(ns);

            this.TraverseIntoMethodBodies = true;
            this.PreorderVisitor = new PreVisitor(this);
        }
        private void AddNamespace(INamespaceDefinition ns)
        {
            if (!(ns is Dummy))
                nss.Add(ns);
        }
        private void AddNamespaceForType(ITypeDefinition td)
        {
            INamespaceMember nm = td as INamespaceMember;
            if (nm != null)
            {
                AddNamespace(nm.ContainingNamespace);
            }
            foreach (var baseType in td.BaseClasses.Union(td.Interfaces))
            {
                AddNamespaceForType(baseType);
            }
        }
        private void AddNamespaceForType(ITypeReference td)
        {
            AddNamespaceForType(td.ResolvedType);
        }

        private void AddNamespacesForMethod(IMethodReference method)
        {
            AddNamespaceForType(method.Type);
            foreach (var p in method.Parameters)
            {
                AddNamespaceForType(p.Type);
            }
            AddNamespaceForType(method.ContainingType);
        }

        public IEnumerable<INamespaceDefinition> Namespaces
        {
            get
            {
                return nss.AsEnumerable();
            }
        }


        #region PreVistor

        private class PreVisitor : CodeVisitor
        {
            private readonly NamespaceCollector nc;
            public PreVisitor(NamespaceCollector nc) { this.nc = nc; }

            public override void Visit(IRootUnitSetNamespace rootUnitSetNamespace)
            {
                nc.AddNamespace(rootUnitSetNamespace);
            }

            public override void Visit(IRootUnitNamespaceReference rootUnitNamespaceReference)
            {
                nc.AddNamespace(rootUnitNamespaceReference.ResolvedUnitNamespace);
            }

            public override void Visit(IRootUnitNamespace rootUnitNamespace)
            {
                nc.AddNamespace(rootUnitNamespace);
            }

            public override void Visit(INestedUnitSetNamespace nestedUnitSetNamespace)
            {
                nc.AddNamespace(nestedUnitSetNamespace);
            }

            public override void Visit(INestedUnitNamespaceReference nestedUnitNamespaceReference)
            {
                nc.AddNamespace(nestedUnitNamespaceReference.ResolvedUnitNamespace);
            }

            public override void Visit(INestedUnitNamespace nestedUnitNamespace)
            {
                nc.AddNamespace(nestedUnitNamespace);
            }

            public override void Visit(INamespaceTypeReference namespaceTypeReference)
            {
                nc.AddNamespace(namespaceTypeReference.ContainingUnitNamespace.ResolvedUnitNamespace);
            }

            public override void Visit(INamespaceTypeDefinition namespaceTypeDefinition)
            {
                nc.AddNamespace(namespaceTypeDefinition.ContainingNamespace);
            }

            public override void Visit(INamespaceAliasForType namespaceAliasForType)
            {
                nc.AddNamespace(namespaceAliasForType.ContainingNamespace);
            }

            public override void Visit(IModuleReference moduleReference)
            {
                nc.AddNamespace(moduleReference.ResolvedModule.UnitNamespaceRoot);
            }

            public override void Visit(IModule module)
            {
                nc.AddNamespace(module.UnitNamespaceRoot);
            }

            public override void Visit(IAssemblyReference assemblyReference)
            {
                nc.AddNamespace(assemblyReference.ResolvedAssembly.UnitNamespaceRoot);
            }

            public override void Visit(IAssembly assembly)
            {
                nc.AddNamespace(assembly.UnitNamespaceRoot);
            }

            public override void Visit(IMethodDefinition method)
            {
                nc.AddNamespacesForMethod(method);
                //method.Body.Dispatch(this);
            }
            public override void Visit(IMethodReference method)
            {
                nc.AddNamespacesForMethod(method);
            }
            public override void Visit(IFieldDefinition fieldDefinition)
            {
                nc.AddNamespaceForType(fieldDefinition.Type);
            }
            public override void Visit(ITypeDefinition typeDefinition)
            {
                nc.AddNamespaceForType(typeDefinition.ResolvedType);
            }
            public override void Visit(ITypeReference typeReference)
            {
                nc.AddNamespaceForType(typeReference);
            }

            public override void Visit(IParameterTypeInformation parameterTypeInformation)
            {
                var typ = parameterTypeInformation.Type.ResolvedType;
                base.Visit(parameterTypeInformation);
            }
        }
        #endregion
    }
}
